#include "stdafx.h"

using namespace System;
using namespace System::Text;
using namespace System::Collections::Generic;
using namespace Microsoft::VisualStudio::TestTools::UnitTesting;

using namespace Gharen::GLCore;

namespace TestMath
{
	[TestClass]
	public ref class MatrixTests
	{
	private:
		TestContext^ testContextInstance;

	public: 
		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		property Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ TestContext
		{
			Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ get()
			{
				return testContextInstance;
			}
			System::Void set(Microsoft::VisualStudio::TestTools::UnitTesting::TestContext^ value)
			{
				testContextInstance = value;
			}
		};

		#pragma region Additional test attributes
		//
		//You can use the following additional attributes as you write your tests:
		//
		//Use ClassInitialize to run code before running the first test in the class
		//[ClassInitialize()]
		//static void MyClassInitialize(TestContext^ testContext) {};
		//
		//Use ClassCleanup to run code after all tests in a class have run
		//[ClassCleanup()]
		//static void MyClassCleanup() {};
		//
		//Use TestInitialize to run code before running each test
		//[TestInitialize()]
		//void MyTestInitialize() {};
		//
		//Use TestCleanup to run code after each test has run
		//[TestCleanup()]
		//void MyTestCleanup() {};
		//
		#pragma endregion 

		// Returns true if the given GLCore matrix and GLM matrix are identical
		// to within epsilon.
		bool AreEqual(Matrix left, glm::mat4 right)
		{
			bool areEqual = true;

			float * leftPtr = (float*)&left;

			float * rightPtr = (float*)&right;

			for(int i = 0; i < 16; i++)
				if(!AreEqual(leftPtr[i], rightPtr[i]))
				{
					areEqual = false;
					Assert::IsTrue(areEqual, "Failed at index: " + i + ". " + leftPtr[i] + " " + rightPtr[i]);
				}

			return areEqual;
		}

		bool AreEqual(float left, float right)
		{
			if(Math::Abs(left-right) < Single::Epsilon)
				return true;
			return false;
		}

		[TestMethod]
		void MatrixMultiply()
		{
			Matrix glcore1 = Matrix(1, 2, 3, 4,
				5, 6, 7, 8,
				9, 10, 11, 12,
				13, 14, 15, 16);
			Matrix glcore2 = Matrix(100, 99, 98, 97,
				96, 95, 94, 93,
				92, 91, 90, 89,
				88, 87, 86, 85);

			Matrix glcoreResult = glcore1 * glcore2;

			glm::mat4 glm1 = glm::mat4(1, 2, 3, 4,
				5, 6, 7, 8,
				9, 10, 11, 12,
				13, 14, 15, 16);

			glm::mat4 glm2 = glm::mat4(100, 99, 98, 97,
				96, 95, 94, 93,
				92, 91, 90, 89,
				88, 87, 86, 85);

			glm::mat4 glmResult = glm1 * glm2;
		
			Assert::IsTrue(AreEqual(glcoreResult, glmResult));
		};

		[TestMethod]
		void MatrixCalculateDeterminant()
		{
			Matrix glcoreMatrix = Matrix(1, 322, 3, 4,
				5, 34, 7, 8,
				9, 10, 11, 12,
				13, 14, 15, 16);

			glm::mat4 glmMatrix = glm::mat4(1, 322, 3, 4,
				5, 34, 7, 8,
				9, 10, 11, 12,
				13, 14, 15, 16);

			Assert::AreEqual(glcoreMatrix.CalculateDeterminant(), glm::determinant(glmMatrix));
		}

		[TestMethod]
		void MatrixCreateRotation()
		{
			Matrix glcoreMatrix = Matrix::CreateRotation(0.5f, Vector3(0.5f, 1.0f, 0.9f));

			glm::mat4 identity = glm::mat4(1);
			glm::mat4 glmMatrix = glm::rotate(identity, 0.5f, glm::vec3(0.5f, 1.0f, 0.9f));

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreateOrthographic()
		{
			Matrix glcoreMatrix = Matrix::CreateOrthographic(34.0f, 999.0f, 444.0f, 0.0f);

			glm::mat4 glmMatrix = glm::ortho(34.0f, 999.0f, 444.0f, 0.0f);

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreateOrthographic2()
		{
			Matrix glcoreMatrix = Matrix::CreateOrthographic(34.0f, 999.0f, 444.0f, 0.0f, -1.0f, 30.0f);

			glm::mat4 glmMatrix = glm::ortho(34.0f, 999.0f, 444.0f, 0.0f, -1.0f, 30.0f);

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreateFrustum()
		{
			Matrix glcoreMatrix = Matrix::CreateFrustum(34.0f, 999.0f, 444.0f, 0.0f, -1.0f, 30.0f);

			glm::mat4 glmMatrix = glm::frustum(34.0f, 999.0f, 444.0f, 0.0f, -1.0f, 30.0f);

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreatePerspective()
		{
			Matrix glcoreMatrix = Matrix::CreatePerspective(0.5f, 0.9f, -1.0f, 33.0f);

			glm::mat4 glmMatrix = glm::perspective(0.5f, 0.9f, -1.0f, 33.0f);

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreatePerspectiveFov()
		{
			Matrix glcoreMatrix = Matrix::CreatePerspectiveFov(0.5f, 30.f, 544.0f, -1.0f, 33.0f);

			glm::mat4 glmMatrix = glm::perspectiveFov(0.5f, 30.f, 544.0f, -1.0f, 33.0f);

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreateLookAt()
		{
			Matrix glcoreMatrix = Matrix::CreateLookAt(Vector3(40.0f, 60.0f, 5.0f),
				Vector3(5.0f, 1.0f, 600.0f), Vector3(45.0f, 16.0f, 3.0f));

			glm::mat4 glmMatrix = glm::lookAt(glm::vec3(40.0f, 60.0f, 5.0f),
				glm::vec3(5.0f, 1.0f, 600.0f), glm::vec3(45.0f, 16.0f, 3.0f));

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreateTranslation()
		{
			Matrix glcoreMatrix = Matrix::CreateTranslation(Vector3(0.5f, 1.0f, 0.9f));

			glm::mat4 identity = glm::mat4(1);
			glm::mat4 glmMatrix = glm::translate(identity, glm::vec3(0.5f, 1.0f, 0.9f));

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreateScale()
		{
			Matrix glcoreMatrix = Matrix::CreateScale(Vector3(0.5f, 1.0f, 0.9f));

			glm::mat4 identity = glm::mat4(1);
			glm::mat4 glmMatrix = glm::scale(identity, glm::vec3(0.5f, 1.0f, 0.9f));

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixTranspose()
		{
			Matrix glcoreMatrix = Matrix(1, 322, 3, 4,
				5, 34, 7, 8,
				9, 10, 11, 12,
				13, 14, 15, 16);

			glm::mat4 glmMatrix = glm::mat4(1, 322, 3, 4,
				5, 34, 7, 8,
				9, 10, 11, 12,
				13, 14, 15, 16);

			Assert::IsTrue(AreEqual(Matrix::Transpose(glcoreMatrix),
				glm::transpose(glmMatrix)));
		}

		[TestMethod]
		void MatrixInverse()
		{
			Matrix glcoreMatrix = Matrix::CreateLookAt(Vector3(10,10,22), Vector3(0,0,0), Vector3(0,1,0))
				* Matrix::CreateOrthographic(0, 500, 500, 0, -1, 100);

			glm::mat4 glmMatrix = glm::lookAt(glm::vec3(10,10,22), glm::vec3(0,0,0), glm::vec3(0,1,0))
				* glm::ortho(0.0f, 500.0f, 500.0f, 0.0f, -1.0f, 100.0f);

			Assert::IsTrue(AreEqual(Matrix::Inverse(glcoreMatrix),
				glm::inverse(glmMatrix)));
		}

		[TestMethod]
		void MatrixCreatePerspectiveInfinite()
		{
			Matrix glcoreMatrix = Matrix::CreatePerspectiveInfinite(0.5f, 0.9f, -1.0f);

			glm::mat4 glmMatrix = glm::infinitePerspective(0.5f, 0.9f, -1.0f);

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}

		[TestMethod]
		void MatrixCreatePick()
		{
			Matrix glcoreMatrix = Matrix::CreatePick(Vector2(100, 200),
				Vector2(10, 99),
				Vector4(10, 15, 1000, 1000));

			glm::mat4 glmMatrix = glm::pickMatrix(glm::vec2(100.0f, 200.0f),
				glm::vec2(10.0f, 99.0f),
				glm::vec4(10.0f, 15.0f, 1000.0f, 1000.0f));

			Assert::IsTrue(AreEqual(glcoreMatrix, glmMatrix));
		}
	};
}
